﻿using System;
using System.IO;
using IOPath = System.IO.Path;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.StorageModel;

namespace System.Web.Hosting
{
    public class CloudVirtualPathProvider : VirtualPathProvider
    {
        private readonly IBlobContainer _container;

        private readonly Dictionary<string, CloudVirtualDirectory> _dirs;
        private readonly Dictionary<string, CloudVirtualFile> _files;

        #region .ctor

        private CloudVirtualDirectory ToCloudDirectory(string path)
        {
            var p = path.LastIndexOfAny(new[] { IOPath.DirectorySeparatorChar, IOPath.AltDirectorySeparatorChar });
            if (p == -1)
                return null;
            var dirPath = path.Substring(p);

            CloudVirtualDirectory dir;
            if (!_dirs.TryGetValue(dirPath, out dir))
            {
                var parent = ToCloudDirectory(dirPath);
                dir = new CloudVirtualDirectory(dirPath);
                _dirs.Add(dirPath, dir);
                if (parent == null)
                    parent._dirs.Add(dir.VirtualPath);
            }
            return dir;
        }

        private CloudVirtualFile ToCloudFile(IBlobSpec spec)
        {
            var path = "~/" + spec.Path.Replace('\\', '/');
            var file = new CloudVirtualFile(path, spec);
            var dir = ToCloudDirectory(path);
            if (dir != null)
                dir._files.Add(path);
            return file;
        }

        public CloudVirtualPathProvider(IBlobContainer container)
        {
            _container = container;
            _dirs = new Dictionary<string, CloudVirtualDirectory>();
            _files = _container.Blobs.Select(ToCloudFile).ToDictionary(file => file.VirtualPath);
            foreach (var dir in _dirs.Values)
            {
                if (!base.DirectoryExists(dir.VirtualPath))
                    continue;
                var baseDir = base.GetDirectory(dir.VirtualPath);
                foreach (string virtualDir in baseDir.Directories)
                    dir._dirs.Add(virtualDir);
                foreach (string virtualPath2 in baseDir.Files)
                    dir._files.Add(virtualPath2);
            }
        }

        #endregion

        public static void Register(IBlobContainer container)
        {
            HostingEnvironment.RegisterVirtualPathProvider(new CloudVirtualPathProvider(container));
        }

        public override bool DirectoryExists(string virtualDir)
        {
            return _dirs.ContainsKey(virtualDir);
        }

        public override bool FileExists(string virtualPath)
        {
            return _files.ContainsKey(virtualPath) || base.FileExists(virtualPath);
        }

        public override VirtualDirectory GetDirectory(string virtualDir)
        {
            CloudVirtualDirectory dir;
            return _dirs.TryGetValue(virtualDir, out dir)
                ? dir
                : base.GetDirectory(virtualDir);
        }

        public override VirtualFile GetFile(string virtualPath)
        {
            CloudVirtualFile file;
            return _files.TryGetValue(virtualPath, out file)
                ? file
                : base.GetFile(virtualPath);
        }
    }

    public class CloudVirtualDirectory : VirtualDirectory
    {
        internal HashSet<string> _dirs = new HashSet<string>();
        internal HashSet<string> _files = new HashSet<string>();

        public CloudVirtualDirectory(string virtualPath)
            : base(virtualPath)
        {
        }

        public override IEnumerable Directories { get { return _dirs; } }
        public override IEnumerable Files { get { return _files; } }
        public override IEnumerable Children
        {
            get { return _dirs.Concat(_files); }
        }
    }

    public class CloudVirtualFile : VirtualFile
    {
        private readonly IBlobSpec _spec;

        public CloudVirtualFile(string virtualPath, IBlobSpec spec)
            : base(virtualPath)
        {
            _spec = spec;
        }

        public override IO.Stream Open()
        {
            return _spec.Read();
        }
    }
}